package vns.server;

import java.util.ArrayList;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import vns.client.service.ObjectService;
import vns.shared.ObjectDTO;
import vns.shared.ObjectPropertyDTO;
import vns.shared.ObjectValueDTO;
import vns.server.domain.Object;
import vns.server.domain.ObjectProperty;
import vns.server.domain.ObjectPropertyType;
import vns.server.domain.ObjectType;
import vns.server.domain.ObjectValue;
import vns.shared.ObjectTypeDTO;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class ObjectServiceImpl extends RemoteServiceServlet implements
		ObjectService {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public ObjectDTO Create(ObjectDTO objectDTO) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Object action = null;
		try {
			action = new Object(objectDTO);
			pm.makePersistent(action);
			if (objectDTO.getListProperty().size() > 0) {
				for (ObjectPropertyDTO pDTO : objectDTO.getListProperty()) {
					for (ObjectValueDTO ovDTO : pDTO.getListObjectValue()) {
						ObjectValue temp = new ObjectValue();
						temp.setObjectKey(KeyFactory.keyToString(action.getId()));
						temp.setPropertyKey(KeyFactory.keyToString(pDTO.getId()));
						temp.setValue(ovDTO.getValue());
						pm.makePersistent(temp);
					}
				}
			}
		} finally {
			pm.close();
		}
		return action.toDTO();
	}

	@Override
	public ObjectDTO Load(Key id) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		ObjectDTO actionDTO = null;
		try {
			Object action = pm.getObjectById(Object.class, id);
			actionDTO = action.toDTO();
		} finally {
			pm.close();
		}
		return actionDTO;
	}

	@SuppressWarnings("unchecked")
	@Override
	public ObjectDTO Update(ObjectDTO objectDTO) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Object action = null;
		ObjectDTO actionDTO = null;
		try {
			if (objectDTO.getId() == null) {
				actionDTO = this.Create(objectDTO);
			} else {
				action = pm.getObjectById(Object.class, objectDTO.getId());
				action.updateFromDTO(objectDTO);
				if (objectDTO.getListProperty().size() > 0) {
					for (ObjectPropertyDTO pDTO : objectDTO.getListProperty()) {

						Query q3 = pm.newQuery(ObjectValue.class);
						q3.setFilter("objectKey == :okey && propertyKey == :pkey");
						List<ObjectValue> listOldValue = (List<ObjectValue>) q3
								.execute(KeyFactory.keyToString(objectDTO
										.getId()), KeyFactory.keyToString(pDTO
										.getId()));
						List<ObjectValueDTO> listOldValueDTO = new ArrayList<ObjectValueDTO>();
						for (ObjectValue v : listOldValue) {
							listOldValueDTO.add(v.toDTO());
						}

						for (ObjectValueDTO ovDTO : pDTO.getListObjectValue()) {

							if (containInList(listOldValueDTO, ovDTO)) {
								// update
								ObjectValue temp = pm.getObjectById(
										ObjectValue.class,
										KeyFactory.keyToString(ovDTO.getId()));
								temp.setValue(ovDTO.getValue());
							} else {
								// create
								ObjectValue temp = new ObjectValue();
								temp.setObjectKey(KeyFactory
										.keyToString(objectDTO.getId()));
								temp.setPropertyKey(KeyFactory.keyToString(pDTO
										.getId()));
								temp.setValue(ovDTO.getValue());
								ObjectValue temp2 = pm.makePersistent(temp);
								ovDTO.setId(temp2.getId());
							}
						}

						for (ObjectValueDTO ovOldDTO : listOldValueDTO) {
							if (!containInList(pDTO.getListObjectValue(),
									ovOldDTO)) {
								// delete
								ObjectValue delValue = pm.getObjectById(
										ObjectValue.class, ovOldDTO.getId());
								if (delValue != null) {
									pm.deletePersistent(pm.getObjectById(
											ObjectValue.class, ovOldDTO.getId()));
								}
							}
						}
					}
				}
			}
		} finally {
			pm.close();
		}
		if (action != null) {
			actionDTO = action.toDTO();
		}
		return actionDTO;
	}

	@Override
	public boolean Remove(Key id) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Object action = pm.getObjectById(Object.class, id);
			if (action != null) {
				pm.deletePersistent(pm.getObjectById(Object.class, id));
			}
		} finally {
			pm.close();
		}
		return true;
	}

	@SuppressWarnings("unchecked")
	@Override
	public ArrayList<ObjectDTO> Loadall(ObjectTypeDTO objecttypeDTO) {
		ArrayList<ObjectDTO> listactionDTO = new ArrayList<ObjectDTO>();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		String temp = KeyFactory.keyToString(objecttypeDTO.getId());
		Query q = pm.newQuery(Object.class);
		q.setFilter("otype == otypeparam");
		q.declareParameters("String otypeparam");

		try {
			if (temp != null && temp != "") {

				List<Object> actions = (List<Object>) q.execute(temp);
				for (Object action : actions) {
					Object term = pm.detachCopy(action);
					ObjectDTO termDTO = term.toDTO();

					List<ObjectProperty> listProperty = new ArrayList<ObjectProperty>();
					Query q2 = pm.newQuery(ObjectProperty.class);
					q2.setFilter("objecttype == otype2param");
					q2.setOrdering("weight asc");
					q2.declareParameters("String otype2param");

					listProperty = (List<ObjectProperty>) q2.execute(KeyFactory
							.keyToString(objecttypeDTO.getId()));
					List<ObjectPropertyDTO> listPropertyDTO = new ArrayList<ObjectPropertyDTO>();
					for (ObjectProperty op : listProperty) {

						ObjectPropertyDTO opDTO = op.toDTO();

						//
						// set Value list
						//
						Query q3 = pm.newQuery(ObjectValue.class);
						q3.setFilter("objectKey == :okey && propertyKey == :pkey");
						List<ObjectValue> listValue = (List<ObjectValue>) q3
								.execute(
										KeyFactory.keyToString(termDTO.getId()),
										KeyFactory.keyToString(opDTO.getId()));
						if (listValue.size() > 1) {

							String values = "";
							for (ObjectValue v : listValue) {
								if (!values.equalsIgnoreCase(""))
									values += ", ";
								values += v.toDTO().getValue();
							}
							opDTO.setValue(values);
						} else if (listValue.size() == 1) {
							opDTO.setValue(listValue.get(0).toDTO().getValue());
							opDTO.setValueKey(KeyFactory.keyToString(listValue
									.get(0).getId()));
						}
						List<ObjectValueDTO> listValueDTO = new ArrayList<ObjectValueDTO>();
						for (ObjectValue ov : listValue) {
							listValueDTO.add(ov.toDTO());
						}
						opDTO.setListObjectValue(listValueDTO);

						//
						// set object Property Type
						//
						ObjectPropertyType opt = pm.getObjectById(
								ObjectPropertyType.class,
								KeyFactory.stringToKey(op.getOptype()));
						opDTO.setOptypename(opt.getBaseType());

						//
						// set Object Type if PropertyType = "key"
						//
						if (opt.getObjecttype() != null
								&& opt.getObjecttype() != "") {
							ObjectType ot = pm
									.getObjectById(ObjectType.class, KeyFactory
											.stringToKey(opt.getObjecttype()));
							opDTO.setObjtypename(ot.getName());
							opDTO.setObjecttype(KeyFactory.keyToString(ot
									.getId()));
						}

						//
						// set list related object
						//
						if (opt.getBaseType().equalsIgnoreCase("key")
								&& opt.getObjecttype() != null
								&& opt.getObjecttype() != "") {
							Query q4 = pm.newQuery(Object.class);
							q4.setFilter("otype == otype4param");
							q4.declareParameters("String otype4param");

							List<Object> listORelated = (List<Object>) q
									.execute(opt.getObjecttype());
							List<ObjectDTO> listORelatedDTO = new ArrayList<ObjectDTO>();
							for (Object oRelated : listORelated) {
								ObjectDTO oRelatedDTO = oRelated.toDTO();

								Query q5 = pm.newQuery(ObjectProperty.class);
								q5.setFilter("objecttype == otype5param");
								q5.declareParameters("String otype5param");
								List<ObjectProperty> listProperty5 = (List<ObjectProperty>) q5
										.execute(oRelatedDTO.getOtype());
								for (ObjectProperty op5 : listProperty5) {

									if (op5.getMain()) {
										ObjectPropertyDTO opDTO5 = op5.toDTO();
										Query q6 = pm
												.newQuery(ObjectValue.class);
										q6.setFilter("objectKey == :okey && propertyKey == :pkey");
										q6.setUnique(true);
										ObjectValue value6 = (ObjectValue) q6
												.execute(
														KeyFactory
																.keyToString(oRelatedDTO
																		.getId()),
														KeyFactory
																.keyToString(opDTO5
																		.getId()));
										oRelatedDTO.setMain(value6.getValue());
									}

								}

								listORelatedDTO.add(oRelatedDTO);
							}
							opDTO.setlistObjectRelated(listORelatedDTO);

							// set value for base type = key
							String value = "";
							for (ObjectValueDTO vDTO : opDTO
									.getListObjectValue()) {
								for (ObjectDTO oDTO : opDTO
										.getlistObjectRelated()) {
									if (vDTO.getValue() != null
											&& oDTO.getId() != null) {
										if (vDTO.getValue().equalsIgnoreCase(
												KeyFactory.keyToString(oDTO
														.getId()))) {
											if (!value.equalsIgnoreCase(""))
												value += ", ";
											value += oDTO.getMain();
										}
									}
								}
							}
							opDTO.setValue(value);

						}

						//
						// set Main Value
						//
						if (op.getMain()) {
							termDTO.setMain(opDTO.getValue());
						}
						listPropertyDTO.add(opDTO);
					}
					termDTO.setListProperty(listPropertyDTO);
					termDTO.setType(objecttypeDTO.getName());
					listactionDTO.add(termDTO);
				}
			}
		} finally {
			q.closeAll();
			pm.close();
		}
		return listactionDTO;
	}

	@Override
	public void Updateall(List<ObjectDTO> listNew, ObjectTypeDTO objecttypeDTO) {
		List<ObjectDTO> listOld = this.Loadall(objecttypeDTO);
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			for (ObjectDTO element : listNew) {
				if (!containInList(listOld, element)) {
					element = this.Create(element);

				} else {
					this.Update(element);
				}
			}

			for (ObjectDTO element : listOld) {
				if (!containInList(listNew, element)) {
					this.Remove(element.getId());
				}
			}
		} finally {
			pm.close();
		}
	}

	private boolean containInList(List<ObjectDTO> list, ObjectDTO element) {
		if (element.getId() == null)
			return false;
		for (ObjectDTO optDTO : list) {
			if (optDTO.getId().equals(element.getId()))
				return true;
		}
		return false;
	}

	private boolean containInList(List<ObjectValueDTO> list,
			ObjectValueDTO element) {
		if (element.getId() == null)
			return false;
		for (ObjectValueDTO vDTO : list) {
			if (vDTO.getId().equals(element.getId()))
				return true;
		}
		return false;
	}

}
